/**
 * ChartConnection
 * @class
 * @param {object} options
 * @property {string[]} urls server urls
 */
function ChartConnection (options){
    var self = this;
    this.chartSocket = null;
    this.isReconnect = false;
    this.retryCount = 0;
    this.logger = console;
    if(!isValued(options) || !isValued(options.urls)){
        throw new Error("Missing connection options");
    }
    this.urls = options.urls;
    this.autoReconnect = true;
    this.token = null;
    this.appId = newUUID();
    for (var property in options) {
        this[property] = options[property];
    }
    self.connectionOnOpenCallBack = null;
    self.connectionOnCloseCallBack = null;
    self.connectionOnErrorCallBack = null;
    self.connectionOnMsgCallBack = null;
    self.messageCount = 0;
    self.totalBytes = 0;
    this.connectionStatus = false;
}

ChartConnection.prototype.registerLifeCycleListener = function (lifeCycleCallback) {
    var self = this;
    if(lifeCycleCallback == null){
        throw new Error("msgListners must not be null");
    }
    self.connectionOnOpenCallBack = lifeCycleCallback.connectionOnOpenCallBack || function(){};
    self.connectionOnCloseCallBack = lifeCycleCallback.connectionOnCloseCallBack || function(){};
    self.connectionOnErrorCallBack = lifeCycleCallback.connectionOnErrorCallBack || function(){};
    self.connectionOnMsgCallBack = lifeCycleCallback.connectionOnMsgCallBack || function(){};
    return self;
}

ChartConnection.prototype.connect = function () {
    var self = this;
    if (self.chartSocket != null) self.chartSocket = null;
    var urls = self.urls;
    var url = null;
    if (urls != null && urls.length > 0) {
        var index = self.retryCount % urls.length;
        url = "ws://" + urls[index] + "/";
    } else {
        self.logger.error(" urls must  be not null");
        throw new Error(" urls must  be not null");
    }
    url += "?appId="+ self.appId;
    if(self.token!=null){
        url+=  "&token="+ self.token;
    }
    self.logger.log("try connect to  ["+url+" ]on ");
    var chartSocket = new WebSocket(url);
    chartSocket.onopen = function (event) {
        self.logger.log("Socket opened , reconnect = [" + self.isReconnect + "]");
        // start timeInterval to deal with server data
        self.connectionStatus = true;
        event.isReconnect = self.isReconnect;
        event.retryCount = self.retryCount;
        self.connectionOnOpenCallBack(event)
    }
    chartSocket.onclose = function (event) {
        self.logger.log("Chart Socket closed", event);
        self.connectionStatus = false;
        self.connectionOnCloseCallBack(event)
        var code = event.code;
        switch (code) {
            case 1003:
            case 4001:
            case 4002:
            case 4004:
            case 3008:
                //TODO tell user connection will not reconnect because ...c
                break;

            default:
                if(self.autoReconnect){
                    self.reconnect(urls);
                }else{
                    self.logger.log()
                }
                break;
        }
    }

    chartSocket.onerror = function (event) {
        console.log("Unable to connect to websocket server");
    };

    chartSocket.onmessage = self.onmessage.bind(self);
    self.chartSocket = chartSocket;

}

ChartConnection.prototype.reconnect = function (urls) {
    var self = this;
    if (!navigator.onLine) {
        // Adding a cool down period on before reconnecting after disconnect
        var coolDownTime = self.randomIntFromInterval(30, 60) * 1000;
        self.logger.info("navigator is offline,Cool down ... Waiting for " + coolDownTime / 1000 + " seconds before attempting to reconnect");
        setTimeout(self.reconnect.bind(this), coolDownTime, urls);
    } else {
        self.isReconnect = true;
        self.retryCount++;
        var coolDownTime = self.randomIntFromInterval(1, 10) * 1000;
        self.logger.info("Cool down ... Waiting for " + coolDownTime / 1000 + " seconds before attempting to reconnect, retryCount = [" + self.retryCount + "]");
        setTimeout(self.connect.bind(this), coolDownTime, urls);
    }

}


ChartConnection.prototype.sendMsg = function (obj) {
    var self = this;
    if (self.chartSocket && self.chartSocket.readyState == WebSocket.OPEN) {
        self.chartSocket.send(JSON.stringify(obj));
    }else{
        self.logger.error("Chart Socket not opened");
    }
}

ChartConnection.prototype.onmessage = function (event) {
    try{
        var self = this;
        var json = JSON.parse(event.data)
        self.messageCount++;
        self.totalBytes += event.data.length;
        if(self.connectionOnMsgCallBack){
            self.connectionOnMsgCallBack(json);
        }
      } catch( e ) {
        console.warn( "Bad Message ", event.data);
      }

}

ChartConnection.prototype.randomIntFromInterval = function (min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
}

ChartConnection.wrap = function (obj, method, func) {
    var proceed = obj[method];
    obj[method] = function () {
        var args = Array.prototype.slice.call(arguments);
        args.unshift(proceed);
        return func.apply(this, args);
    };
}

/**
 *
 *
 * @param {*} callbacks
 */
function LifeCycleCallback(callbacks){
    self.connectionOnOpenCallBack = callbacks.connectionOnOpenCallBack;
    self.connectionOnCloseCallBack = callbacks.connectionOnCloseCallBack;
    self.connectionOnErrorCallBack = callbacks.connectionOnErrorCallBack;
    self.connectionOnMsgCallBack = callbacks.connectionOnMsgCallBack;
}